Skip to content

fix(vite): don't inject source attrs into angle brackets inside strings/comments - #8

Merged
Peyton-Spencer merged 1 commit into
mainfrom
fix/skip-jsx-scan-in-string-literals
Jun 22, 2026
Merged

fix(vite): don't inject source attrs into angle brackets inside strings/comments#8
Peyton-Spencer merged 1 commit into
mainfrom
fix/skip-jsx-scan-in-string-literals

Conversation

@Peyton-Spencer

@Peyton-Spencer Peyton-Spencer commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

The bug

The Vite plugin's JSX scanner (transformJsx) matches <Tag with a raw-text
regex over the whole file — it doesn't know about string/comment context. When
a < happens to sit after an operator-like character, isLikelyJsx() waves it
through and a data-solid-source="..." attribute gets injected into whatever it
landed on, even inside a string.

A real-world trigger is a CLI command built with a template literal containing a
<owner>/<repo> placeholder:

const ghCmd = () =>
  `gh secret set ${shq(name())} --repo ${shq(repo() || "<owner>/<repo>")} --body ${shq(key())}`

<repo> matches the regex; isLikelyJsx() walks back, sees the / before it
(treated as an operator), and decides it's JSX. The plugin rewrites the string to:

... "<owner>/<repo data-solid-source="src/.../CIInstructions.tsx:94:..." >" ...

The injected " terminates the JS string and the downstream Babel parse dies:

[plugin:solid] Unexpected token, expected "," (94:91)

The fix

Add buildLiteralMask() — a small linear scanner that marks every character
living inside a string literal, template-literal text, or comment. The match
loop now skips any < whose position is masked.

Template-literal interpolations (${ ... }) are treated as code, so genuine
JSX inside an interpolation still gets its source/component attributes. Handles
single/double quotes, escapes, line/block comments, and nested ${}.

Regex literals are intentionally not tracked — disambiguating regex from
division needs a real tokenizer, and <tag> inside a regex literal is far rarer
than inside strings/comments. Noted as a known limitation in the code.

Tests

bun test → 62 pass. New regression cases in tests/vite-plugin.test.ts:

  • angle brackets inside a plain string → no injection
  • the <owner>/<repo> placeholder inside a template literal → string left
    intact, sibling real JSX still gets a source attr
  • angle brackets inside // and /* */ comments → no injection
  • JSX inside a ${ } interpolation → still injected

Verified end-to-end against the file that surfaced this: the rebuilt plugin
leaves the placeholder untouched and the output parses cleanly.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed JSX attribute injection to correctly skip angle brackets appearing in string literals, template literals, and comments.
  • Tests

    • Added comprehensive test coverage for edge cases involving angle brackets within literals and comments.

The regex JSX scanner matched `<Tag` anywhere in the source, including
inside string literals, template-literal text, and comments. A `<` that
sits after an operator-like char (e.g. the `/` in a `<owner>/<repo>`
placeholder) passed isLikelyJsx() and got a `data-solid-source="..."`
attribute injected straight into the string. The injected `"` terminated
the JS string and crashed the downstream Babel parse with
`Unexpected token, expected ","`.

Add buildLiteralMask(): a small linear scanner that marks every character
inside a string, template-literal text, or comment, and skip any regex
match whose `<` falls in a masked region. Template-literal interpolations
(`${ ... }`) are still treated as code, so real JSX there keeps its source
attributes. Regex literals remain a known (rarer) edge case.

Adds regression tests for the placeholder-in-template case plus string,
comment, and interpolation coverage.
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc77c164-ce79-42bd-9f83-bda291c04aaf

📥 Commits

Reviewing files that changed from the base of the PR and between 05efd46 and 08b3c37.

📒 Files selected for processing (2)
  • src/vite.ts
  • tests/vite-plugin.test.ts

📝 Walkthrough

Walkthrough

Adds a buildLiteralMask(code) function to src/vite.ts that uses a mode-stack state machine to mark source positions inside string literals, template literal text, and comments. transformJsx now calls this once per file and skips JSX attribute injection for any regex match whose < index is masked. Four new test cases cover the edge conditions.

Changes

Literal-aware masking for JSX injection

Layer / File(s) Summary
buildLiteralMask state machine and transformJsx integration
src/vite.ts
New buildLiteralMask tokenizes source with a mode-stack state machine (single/double-quoted strings, template literal text, line/block comments, ${...} interpolation nesting) returning a mask array. transformJsx precomputes the mask and skips attribute injection when the matched < offset is masked.
Tests for masked literal and comment regions
tests/vite-plugin.test.ts
Four new test cases verify: no transform for angle brackets in a string literal; template placeholder <owner>/<repo> unchanged while JSX in the interpolation still gets data-solid-source; no injection for angle brackets in comments; JSX inside a template interpolation is injected while surrounding literal <b> text is untouched.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 A rabbit hops through strings and quotes,
Sniffing out each < that floats —
"Is this JSX or just some text?
A comment? Template? What comes next?"
With mask in paw, it skips the fakes,
And only injects where real JSX wakes! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main fix: preventing incorrect injection of source attributes into angle brackets that appear inside strings and comments, which is the core objective of this changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/skip-jsx-scan-in-string-literals

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Peyton-Spencer
Peyton-Spencer merged commit 38886d8 into main Jun 22, 2026
2 checks passed
@Peyton-Spencer
Peyton-Spencer deleted the fix/skip-jsx-scan-in-string-literals branch June 22, 2026 05:10
github-actions Bot pushed a commit that referenced this pull request Jun 22, 2026
## [1.1.3](v1.1.2...v1.1.3) (2026-06-22)

### Bug Fixes

* **vite:** don't inject into angle brackets inside strings/comments ([#8](#8)) ([38886d8](38886d8))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant